#!/usr/bin/python

"""
This is intended as a sample for the MultiColumnList some day, though it's heavy on straight wxPython right now

Goal: imitate the Windows utility "nPOP" (hence the temporary code name), with the following differences :
"""
__version__ = "$Revision: 1.23 $"
__date__ = "$Date: 2005/12/13 11:13:23 $"

from PythonCard import dialog, log, model
import wx

import fpoputil
from email.MIMEText import MIMEText
import os

class Preview(model.Background):

    def on_initialize(self, event):
        self.parent = self.GetParent()
        # openBackground could possibly occur
        # after displayMessage call below
        if not hasattr(self, 'msg'):
            self.msg = None
            self.msgNumber = None
            self.headers = None
        self.initSizers()

    # this isn't quite right, the wxFlexGridSizer
    # containing the headers should expand horizontally as the window
    # expands
    def initSizers(self):
        comp = self.components
        
        sizer1 = wx.BoxSizer(wx.VERTICAL)
        sizer2 = wx.FlexGridSizer(5, 2, 3, 10)
        # AddGrowableCol doesn't seem to do anything
        # I want the 2nd column to expand as the window gets wider
        # so the To, Cc, and Subject fields get larger
        sizer2.AddGrowableCol(1)
        sizer3 = wx.BoxSizer(wx.HORIZONTAL)
        #self.sizerAttachments = wx.BoxSizer(wx.HORIZONTAL)
        
        stcSizerAttrs = wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL         
        fldSizerAttrs = wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND
        vertFlags = wx.LEFT | wx.TOP | wx.ALIGN_LEFT
        chkSizerAttrs = wx.RIGHT | wx.ALIGN_CENTER_VERTICAL

        #self.sizerAttachments.Add(comp.listAttachments, 1, chkSizerAttrs, 20)
        #self.sizerAttachments.Add(comp.btnSave, 0, chkSizerAttrs, 20)
        #self.sizerAttachments.Add(comp.btnOpen, 0, chkSizerAttrs, 20)
        
        sizer3.Add(comp.fldFrom, 1, chkSizerAttrs, 20)
        sizer3.Add((5, 5), 0)  # spacer
        sizer3.Add(comp.stcDate, 0, chkSizerAttrs, 20)
        sizer3.Add(comp.fldDate, 0, wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 20)

        sizer2.Add(comp.stcSender, flag=stcSizerAttrs)
        sizer2.Add(comp.fldSender, flag=fldSizerAttrs)
        sizer2.Add(comp.stcFrom, flag=stcSizerAttrs)
        sizer2.Add(sizer3, flag=fldSizerAttrs)
        sizer2.Add(comp.stcTo, flag=stcSizerAttrs)
        sizer2.Add(comp.fldTo, flag=fldSizerAttrs)
        sizer2.Add(comp.stcCc, flag=stcSizerAttrs)
        sizer2.Add(comp.fldCc, flag=fldSizerAttrs)
        sizer2.Add(comp.stcSubject, flag=stcSizerAttrs)
        sizer2.Add(comp.fldSubject, flag=fldSizerAttrs)

        sizer1.Add(sizer2, 0, wx.EXPAND) #vertFlags)
        sizer1.Add((5, 5), 0)  # spacer
        sizer1.Add(comp.fldBody, 1, wx.EXPAND)

        #sizer1.Add(self.sizerAttachments, 0, wx.EXPAND)
        self.sizer1 = sizer1

        sizer1.Fit(self)
        sizer1.SetSizeHints(self)
        self.panel.SetSizer(sizer1)
        self.panel.SetAutoLayout(1)
        self.panel.Layout()

    def on_menuFileClose_select(self, event):
        self.close()

    # we should be able to get all the headers from the msg
    # I just haven't figured out how to do it yet
    def on_displayHeaders_command(self, event):
        if self.headers is not None:
            dialog.scrolledMessageDialog(self, self.headers, 'Message Headers')

    def on_delete_command(self, event):
        if self.msgNumber is not None:
            listX = self.parent.components.mclHeaders
            # have to find the message in the list
            # since what we're previewing might not be the same
            # as the currently selected item in the list
            # FindItemData will find the actual line number
            # which is 0 based
            # FindItemData is broken in 2.3.3pre5
            #print self.msgNumber, list.FindItemData(-1, self.msgNumber)
            for i in range(0, listX.GetItemCount()):
                if listX.GetItemData(i) == self.msgNumber:
                    listX.SetItemImage(i, 2, 2)
                    break

    def clearFields(self):
        for field in ['fldSender', 'fldFrom', 'fldTo', 'fldCc', 'fldSubject', 'fldDate', 'fldBody']:
            self.components[field].text = ''

    def showAttachments(self):
        #print self.attachments
        comp = self.components
        if not comp.listAttachments.visible:
            comp.listAttachments.visible = 1
            comp.btnSave.visible = 1
            comp.btnOpen.visible = 1

            chkSizerAttrs = wx.RIGHT | wx.ALIGN_CENTER_VERTICAL
            self.sizerAttachments = wx.BoxSizer(wx.HORIZONTAL)
            self.sizerAttachments.Add(comp.listAttachments, 1, chkSizerAttrs, 20)
            self.sizerAttachments.Add(comp.btnSave, 0, chkSizerAttrs, 20)
            self.sizerAttachments.Add(comp.btnOpen, 0, chkSizerAttrs, 20)
            self.sizer1.Add(self.sizerAttachments, 0, wx.EXPAND)
            self.sizer1.Fit(self)
        filenames = []
        for a in self.attachments:
            filenames.append(a['filename'])
        comp.listAttachments.items = filenames
        comp.listAttachments.selection = 0

    def hideAttachments(self):
        #print "hide", self.attachments
        comp = self.components
        if comp.listAttachments.visible:
            comp.listAttachments.visible = 0
            comp.btnSave.visible = 0
            comp.btnOpen.visible = 0
            self.sizer1.Remove(self.sizerAttachments)
            self.sizer1.Fit(self)

    def saveAttachment(self, n):
        # decode the payload of part
        partNumber = self.attachments[n]['part']
        filename = self.attachments[n]['filename']

        wildcard = "All Files (*.*)|*.*"
        dir = self.application.applicationDirectory
        result = dialog.saveFileDialog(None, "Save As", dir, filename, wildcard)
        if result.accepted:
            path = result.paths[0]
        else:
            # error message here?
            return

        #print "part:", partNumber
        #print "is_multipart", self.msg.is_multipart()
        print self.attachments[n]
        ## get_payload isn't working for some reason
        payload = self.msg.get_payload(partNumber, decode=1)
        print len(payload)

        # but walking manually does?!
        counter = 0
        for part in self.msg.walk():
            # multipart/* are just containers
            partType = part.get_type()
            if part.get_main_type() == 'multipart':
                continue
            if counter == partNumber:
                payload = part.get_payload(decode=1)
                break
            counter += 1

        fp = open(path, 'wb')
        fp.write(payload)
        fp.close()
        print "wrote", len(payload), "bytes to", path

    def on_btnSave_mouseClick(self, event):
        sel = self.components.listAttachments.GetSelection()
        self.saveAttachment(sel)

    # see the email module documentation
    # http://docs.python.org/lib/module-email.html
    # this probably isn't the best way to put together a message
    def displayMessage(self, msg, msgNumber, headers):
        self.msg = msg
        self.msgNumber = msgNumber
        self.headers = headers
        self.clearFields()

        comp = self.components
        comp.fldSender.text = fpoputil.joinList(msg.get_all('sender'))
        comp.fldFrom.text = fpoputil.joinList(msg.get_all('from'))
        comp.fldTo.text = fpoputil.joinList(msg.get_all('to'))
        comp.fldCc.text = fpoputil.joinList(msg.get_all('cc'))
        comp.fldSubject.text = msg.get('subject')
        comp.fldDate.text = fpoputil.dateString(msg.get('date'))

        text = fpoputil.getBody(msg)            
        comp.fldBody.text = text

        self.attachments = fpoputil.getAttachments(msg)
        if self.attachments != []:
            self.showAttachments()
        else:
            self.hideAttachments()

    def quoteorigtext(self):
        comp = self.components
        
        bodytext = comp.fldBody.text
        quoted   = '\n' + fpoputil.ORIGINAL_MESSAGE_HEADER + '\n'
        for hdr in ('From', 'To', 'Subject', 'Date'):
            quoted = quoted + ('%s: %s\n' % (hdr, self.msg.get(hdr, '?')))
        attribution = self.msg.get('x-attribution')
        if attribution is not None and attribution != '':
            # print "attribution:", attribution
            # modify quoting to do
            #     KA> message line
            #     KA> message line 2
            # style quoting, look at Barry Warsaw examples
            # below I'm using four spaces
            #bodytext = bodytext.replace('\n', '\n    ' + attribution + '> ')
            #quoted = '\n' + quoted.replace('\n', '\n> ')
            #quoted   = quoted + bodytext
            quoted   = quoted + '\n' + bodytext
            quoted   = '\n' + quoted.replace('\n', '\n> ')
        else:
            quoted   = quoted + '\n' + bodytext
            quoted   = '\n' + quoted.replace('\n', '\n> ')
        return quoted 

    def buildReplyMessage(self, toAll=0, toGroup=0):
        msg = MIMEText(self.quoteorigtext())
        # the reply address needs to be more sophisticated to
        # deal with multiple To, Ccs, etc.
        if toGroup:
            to = self.msg.get('mail-followup-to')
            if to is None:
                to = self.msg.get('to', '')
                from_ = self.msg.get('reply-to', '')
                if not from_:
                    from_ = self.msg.get('from', '')
                if to and from_:
                    to = to + ', ' + from_
        else:
            to = self.msg.get('reply-to')
            if to is None:
                to = self.msg.get('from', '')
        # need to filter out duplicates
        # this doesn't do that, adapted from pms
        #msg['to'] = fpoputil.filteredUser(self.parent.fromAddress, to)
        #print 'to', msg['to']
        msg['to'] = to

        if toGroup or not toAll:
            pass
        else:
            to = self.msg.get('to', '')
            cc = self.msg.get('cc', '')
            if to and cc:
                cc = to + '; ' + cc
            else:
                cc = to + cc
            # need to filter out duplicates
            msg['cc'] = cc

        subject = self.msg.get('Subject', '(no subject)')
        if not subject.lower().startswith('re:'):
            subject = 'Re: ' + subject
        msg['subject'] = subject
        
        references = fpoputil.getReferences(self.msg)
        if references is not None:
            msg['references'] = references

        inReplyTo = fpoputil.getInReplyTo(self.msg)
        if inReplyTo is not None:
            msg['in-reply-to'] = inReplyTo

        # should also set the In-Reply-To or whatever else is
        # required to make sure message threads work right
        messageWindow = self.parent.createMessageWindow(msg)
        
    def on_reply_command(self, event):
        self.buildReplyMessage()

    def on_replyGroup_command(self, event):
        self.buildReplyMessage(toGroup=1)
        
    def on_replyAll_command(self, event):
        self.buildReplyMessage(toAll=1)

    def on_forward_command(self, event):
        comp = self.components
        
        bodytext = comp.fldBody.text
        quoted   = '\n' + fpoputil.ORIGINAL_MESSAGE_HEADER + '\n'
        for hdr in ('From', 'To', 'Subject', 'Date'):
            quoted = quoted + ('%s: %s\n' % (hdr, self.msg.get(hdr, '?')))
        quoted   = quoted + '\n' + bodytext
        
        msg = MIMEText(quoted)
        
        subject = self.msg.get('Subject', '(no subject)')
        if not subject.lower().startswith('fwd:'):
            subject = 'Fwd: ' + subject
        msg['subject'] = subject

        """
        # should forwarded messages have
        # references and in-reply-to???
        
        references = fpoputil.getReferences(self.msg)
        if references is not None:
            msg['references'] = references

        inReplyTo = fpoputil.getInReplyTo(self.msg)
        if inReplyTo is not None:
            msg['in-reply-to'] = inReplyTo
        """

        # forwarded messages should have attachments
        # how do we go about duplicating the attachments from
        # the original message?

        # should also set the In-Reply-To or whatever else is
        # required to make sure message threads work right
        messageWindow = self.parent.createMessageWindow(msg)

    def on_close(self, event):
        self.visible = False

